﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using DigitalRune.Windows.Docking;

namespace SharpLab
{
    public partial class GraphicForm : DockableForm
    {
        #region private Deklarationen
        #region Fließkommavariablen
        /// <summary>
        /// Minimum für die X-Achse
        /// </summary>
        private float _XMin = -10;
        /// <summary>
        /// Maximum für die X-Achse
        /// </summary>
        private float _XMax = 10;
        /// <summary>
        /// Minimum für die Y-Achse
        /// </summary>
        private float _YMin = -10;
        /// <summary>
        /// Maximum für die Y-Achse
        /// </summary>
        private float _YMax = 10;
        /// <summary>
        /// Skaleneinteilung der X-Achse
        /// </summary>
        private float _XScale = 1;
        /// <summary>
        /// Skaleneinteilung der Y-Achse
        /// </summary>
        private float _YScale = 1;
        /// <summary>
        /// Skalierungsfaktor für die Darstellung der X-Koordinaten. Er wird aus Fensterbreite geteilt durch 
        /// den darzustellenden Definitionsbereich errechnet.
        /// </summary>
        private float _XStep;
        /// <summary>
        /// Skalierungsfaktor für die Darstellung der Y-Koordinaten. Er wird aus der Fensterbreite geteilt durch
        /// den darzustellenden Wertebereich errechnet.
        /// </summary>
        private float _YStep;
        #endregion
        #region Boole'sche Variablen
        /// <summary>
        /// Horizontale Gittelinien An bzw. Aus.
        /// </summary>
        private bool _hGrid = true;
        /// <summary>
        /// Vertikale Gitterlinien An bzw. Aus.
        /// </summary>
        private bool _vGrid = true;
        /// <summary>
        /// Mauskoordinaten anzeigen oder ausblenden
        /// </summary>
        private bool _MouseCoordinates = true;
        /// <summary>
        /// Mauszeiger befindet sich im Bereich des Fensters.
        /// </summary>
        private bool _MouseInWindow = false;
        /// <summary>
        /// Messcursor für X-Achse anzeigen
        /// </summary>
        private bool _XCursor = false;
        /// <summary>
        /// Cursor soll bewegt werden
        /// </summary>
        private bool _MoveCursor = false;
        /// <summary>
        /// Ist true, wenn sich der Mauszeiger +- 10 Pixel vom Cursor entfernt befindet
        /// </summary>
        private bool _MouseOverCursor = false;
        #endregion
        #region String Variablen
        /// <summary>
        /// Name des Diagramms
        /// </summary>
        private string _ChartName;
        /// <summary>
        /// Bezeichnung der X-Achse
        /// </summary>
        private string _XLabel = "x";
        /// <summary>
        /// Bezeichnung der Y-Achse
        /// </summary>
        private string _YLabel = "y";
        /// <summary>
        /// Koordinateneinheit der X-Achse
        /// </summary>
        private string _XUnit = "mm";
        /// <summary>
        /// Koordinateneinheit der Y-Achse
        /// </summary>
        private string _YUnit = "mm";
        #endregion
        #region PointF- bzw. Point- Variablen
        /// <summary>
        /// Mittelpunkt des Koordinatensystems
        /// </summary>
        private PointF Middle;
        /// <summary>
        /// Punkt links in der Mitte des Koordinatensystems
        /// </summary>
        private PointF Left;
        /// <summary>
        /// Rechter Punkt des Koordinatensystems
        /// </summary>
        private PointF Right;
        /// <summary>
        /// Oberster Punkt des Koordinatensystems
        /// </summary>
        private PointF Top;
        /// <summary>
        /// Unterster Punkt des Koordinatensystems
        /// </summary>
        private PointF Buttom;
        /// <summary>
        /// Stützpunkte für die Darstellung der Kurve
        /// </summary>
        private PointF[] _Data;
        /// <summary>
        /// Position der Maus. Diese wird aktualisiert, wenn die Maus sich innerhalb des Fensters befindet
        /// </summary>
        private Point _MousePos;
        /// <summary>
        /// Position des Cursors
        /// </summary>
        private PointF _CursorPos = new Point(100, 0);
        #endregion

        #endregion

        #region Die Schnittstelle nach Aussen
        #region Properties
        /// <summary>
        /// legt den kleinsten X-Wert für das Koordinatensystem fest.
        /// </summary>
        /// <remarks>
        /// Ist XMin größer XMax, dann wird XMin gleich XMax gesetzt!
        /// </remarks>
        public float XMin
        {
            set
            {
                if (XMin <= _XMax) _XMin = value;
                else XMin = _XMax - 10;
            }
            get
            {
                return _XMin;
            }
        }
        /// <summary>
        /// legt den größten X-Wert für das Koordinatensystem fest.
        /// </summary>
        /// <remarks>
        /// Ist XMax kleiner XMin, dann wird XMin gleich XMax gesetzt!
        /// </remarks>
        public float XMax
        {
            set
            {
                if (XMax >= _XMin) _XMax = value;
                else _XMax = _XMin;
            }
            get
            {
                return _XMax;
            }
        }
        /// <summary>
        /// legt den kleinsten Y-Wert für das Koordinatensystem fest.
        /// </summary>
        /// <remarks>
        /// Ist YMin größer YMax, dann wird YMin gleich YMax gesetzt!
        /// </remarks>
        public float YMin
        {
            set
            {
                if (YMin <= _YMax) _YMin = value;
                else YMin = _YMax;
            }
            get
            {
                return _YMin;
            }
        }
        /// <summary>
        /// legt den größten Y-Wert für das Koordinatensystem fest.
        /// </summary>
        /// <remarks>
        /// Ist YMax kleiner YMin, dann wird YMin gleich YMax gesetzt!
        /// </remarks>
        public float YMax
        {
            set
            {
                if (YMax >= _YMin) _YMax = value;
                else _YMax = _YMin;
            }
            get
            {
                return _YMax;
            }
        }
        /// <summary>
        /// Legt die Skaleneinteilung für die X-Achse fest
        /// </summary>
        public float XScale
        {
            set
            {
                if (XScale > 0) _XScale = value;
                else _XScale = 10;
            }
            get
            {
                return _XScale;
            }
        }
        /// <summary>
        /// Legt die Skaleieinteilung für die Y-Achse fest
        /// </summary>
        public float YScale
        {
            set
            {
                if (YScale > 0) _YScale = value;
                else _YScale = 10;
            }
            get
            {
                return _YScale;
            }
        }
        /// <summary>
        /// vertikales Gitternetz anzeigen oder ausblenden
        /// </summary>
        public bool hGrid
        {
            set
            {
                if (_hGrid != value) _hGrid = value;
            }
            get
            {
                return _hGrid;
            }
        }
        /// <summary>
        /// horizontales Gitternetz anzeigen oder ausblenden
        /// </summary>
        public bool vGrid
        {
            set
            {
                if (_vGrid != value) _vGrid = value;
            }
            get
            {
                return _vGrid;
            }
        }
        /// <summary>
        /// Bezeichnung des Diagramms
        /// </summary>
        public string ChartName
        {
            set
            {
                _ChartName = value;
            }
            get
            {
                return _ChartName;
            }
        }
        /// <summary>
        /// Beschriftung der X-Achse
        /// </summary>
        public string XLabel
        {
            set
            {
                _XLabel = value;
            }
            get
            {
                return _XLabel;
            }
        }
        /// <summary>
        /// Beschriftung der Y-Achse
        /// </summary>
        public string YLabel
        {
            set
            {
                _YLabel = value;
            }
            get
            {
                return _YLabel;
            }
        }
        /// <summary>
        /// Einheit der X-Achse
        /// </summary>
        public string XUnit
        {
            set
            {
                _XUnit = value;
            }
            get
            {
                return _XUnit;
            }
        }
        /// <summary>
        /// Einheit der Y-Achse
        /// </summary>
        public string YUnit
        {
            set
            {
                _YUnit = value;
            }
            get
            {
                return _YUnit;
            }
        }
        /// <summary>
        /// Daten zum zeichnen
        /// </summary>
        public PointF[] Data
        {
            set
            {
                _Data = value;
            }
            get
            {
                return _Data;
            }
        }
        /// <summary>
        /// Mauskoordinaten an- oder ausschalten
        /// </summary>
        public bool MouseCoordinates
        {
            set
            {
                _MouseCoordinates = value;
            }
            get
            {
                return _MouseCoordinates;
            }
        }
        /// <summary>
        /// Cursor füe die X-Achse anzeigen
        /// </summary>
        public bool XCursor
        {
            set
            {
                _XCursor = value;
            }
            get
            {
                return _XCursor;
            }
        }

        #endregion
        #region Events
        public event EventHandler ShowOptions;

        #endregion

        #endregion


        public GraphicForm()
        {
            InitializeComponent();
        }

        #region protected Methoden
        /// <summary>
        /// Routine für das Event OnShowOptions
        /// </summary>
        /// <param name="e">Ereignisargumente</param>
        protected virtual void OnShowOptions(EventArgs e)
        {
            if (ShowOptions != null)
                ShowOptions(this, e);
        }
        #endregion

        #region öffentliche Methoden
        /// <summary>
        /// Ändert den Namen des Diagramms. Er wird in den Tabs bzw. den Fenstertiteln angezeigt
        /// </summary>
        public void setChartName()
        {
            TabText = _ChartName;
        }
        #endregion

        #region private Methoden
        /// <summary>
        /// Laden des Fensters
        /// </summary>
        /// <param name="sender">Sender</param>
        /// <param name="e">Ereignisargumente</param>
        private void GraphicForm_Load(object sender, EventArgs e)
        {
            
        }
        /// <summary>
        /// Das Fenster wird neu gezeichnet
        /// </summary>
        /// <param name="sender">Sender</param>
        /// <param name="e">Ereignisargumente</param>
        private void GraphicForm_Paint(object sender, PaintEventArgs e)
        {
            _DrawKoS(e.Graphics);
            if(Data != null)
                _DrawData(Data, e.Graphics);
            if(_MouseCoordinates && _MouseInWindow)
                _DrawMouseCoordinates(e.Graphics);
            if (_XCursor)
                _DrawMeasurementCursor(e.Graphics);
        }
        /// <summary>
        /// Wird aufgerufen, wenn die Größe des Fensters geändert wird
        /// </summary>
        /// <param name="sender">Sender</param>
        /// <param name="e">Ereignisargumente</param>
        private void GraphicForm_Resize(object sender, EventArgs e)
        {
            _RecalculatePoints(true);
            Invalidate(this.ClientRectangle);
        }
        /// <summary>
        /// Zeichnet das Koordinatensystem
        /// </summary>
        /// <param name="gk">Grafikkomponente</param>
        private void _DrawKoS(Graphics gk)
        {
            _RecalculatePoints(false);

            float j = 0; //Zähler für Skalenbeschriftung

            //X-Achse
            gk.DrawLine(new Pen(Color.Black), Left, Right);
            //Y-Achse
            gk.DrawLine(new Pen(Color.Black), Top, Buttom);

            Pen grp = new Pen(Color.LightGray);
            grp.DashStyle = DashStyle.Dash;

            // X-Achsen Skalenstriche in pos. Richtung
            j = _XScale;
            for (float i = (Middle.X + (_XStep * _XScale)); i <= Right.X; i += (_XStep * _XScale))
            {
                //wenn vGrid True, dann vertikales Gitter zeichnen
                if (_vGrid)
                {
                    gk.DrawLine(grp, i, Buttom.Y, i, Top.Y);
                }

                //Skalenstriche
                gk.DrawLine(new Pen(Color.Black), i, Middle.Y, i, Middle.Y + 4);

                //Beschriftung
                SizeF length = gk.MeasureString(j.ToString("F02"), new Font("Arial", 8));
                gk.DrawString(j.ToString("F02"), new Font("Arial", 8), new SolidBrush(Color.Black), i - (length.Width / 2), Middle.Y + 4);

                j += _XScale;
            }

            // X-Achsen Skalenstriche in neg. Richtung
            j = _XScale;
            for (float i = (Middle.X - (_XStep * _XScale)); i >= Left.X; i -= (_XStep * _XScale))
            {
                //wenn vGrid True, dann vertikales Gitter zeichnen
                if (_vGrid)
                {
                    gk.DrawLine(grp, i, Buttom.Y, i, Top.Y);
                }
                //Skalenstrich
                gk.DrawLine(new Pen(Color.Black), i, Middle.Y, i, Middle.Y + 4);
                //Beschriftung
                SizeF length = gk.MeasureString(j.ToString("F02"), new Font("Arial", 8));
                gk.DrawString((-j).ToString("F02"), new Font("Arial", 8), new SolidBrush(Color.Black), i - (length.Width / 2), Middle.Y + 4);

                j += _XScale;
            }

            // Y-Achsen Skalenstriche in pos. Richtung
            j = _YScale;
            for (float i = (Middle.Y - (_YStep * _YScale)); i >= Top.Y; i -= (_YStep * _YScale))
            {
                //Wenn hGrid True, dann horizontales Gitter zeichnen
                if (_hGrid)
                {
                    gk.DrawLine(grp, Left.X, i, Right.X, i);
                }
                //Skalenstrich
                gk.DrawLine(new Pen(Color.Black), Middle.X, i, Middle.X - 4, i);
                //Beschriftung
                SizeF length = gk.MeasureString(j.ToString("F02"), new Font("Arial", 8));
                gk.DrawString(j.ToString("F02"), new Font("Arial", 8), new SolidBrush(Color.Black), Middle.X - (length.Width * 2f), i - (length.Height / 2));

                j += _YScale;
            }

            // Y-Achsen Skalenstriche in neg. Richtung
            j = _YScale;
            for (float i = (Middle.Y + (_YStep * _YScale)); i <= Buttom.Y; i += (_YStep * _YScale))
            {
                //Wenn hGrid True, dann horizontales Gitter zeichnen
                if (_hGrid)
                {
                    gk.DrawLine(grp, Left.X, i, Right.X, i);
                }
                //Skalenstrich
                gk.DrawLine(new Pen(Color.Black), Middle.X, i, Middle.X - 4, i);
                //Beschriftung
                SizeF length = gk.MeasureString(j.ToString("F02"), new Font("Arial", 8));
                gk.DrawString((-j).ToString("F02"), new Font("Arial", 8), new SolidBrush(Color.Black), Middle.X - (length.Width * 2f), i - (length.Height / 2));

                j += _YScale;
            }

            //Achsenbeschriftung und Einheit
            //X-Achse
            SizeF lengthT = gk.MeasureString(_XLabel + " / " + _XUnit, new Font("Arial", 8));
            gk.DrawString(_XLabel + " / " + _XUnit, new Font("Arial", 8), new SolidBrush(Color.Black), Right.X - lengthT.Width, Right.Y + lengthT.Height);
            //Y-Achse
            lengthT = gk.MeasureString(_YLabel + " / " + _YUnit, new Font("Arial", 8));
            gk.DrawString(_YLabel + " / " + _YUnit, new Font("Arial", 8), new SolidBrush(Color.Black), Top.X - lengthT.Width, Top.Y);
        }
        /// <summary>
        /// Punkte für das Koordinatensystem und die Cursor neu berechnen
        /// </summary>
        private void _RecalculatePoints(bool _Resized)
        {
            _XStep = Width / (Math.Abs(_XMin) + Math.Abs(_XMax));
            _YStep = Height / (Math.Abs(_YMin) + Math.Abs(_YMax));
            Middle = new PointF(Math.Abs(_XMin) * _XStep, ((Math.Abs(_YMax) - Math.Abs(_YMin)) * _YStep + (_YStep * Math.Abs(_YMin))));
            Left = new PointF(0, Middle.Y);
            Right = new PointF(Width, Middle.Y);
            Top = new PointF(Middle.X, 0);
            Buttom = new PointF(Middle.X, Height);

            //Cursor X-Position aktualisieren
            
        
           
        }
        /// <summary>
        /// Einstellungen werden aufgerufen
        /// </summary>
        /// <param name="sender">Sender</param>
        /// <param name="e">Ereignisargumente</param>
        private void einstellungenToolStripMenuItem_Click(object sender, EventArgs e)
        {
            OnShowOptions(new EventArgs());
        }
        /// <summary>
        /// Zeichnet Daten von einem PointF-Array
        /// </summary>
        /// <param name="Data">Feld mit Punkten vom Typ PointF</param>
        /// <param name="gr">Grafikkomponente</param>
        private void _DrawData(PointF[] Data, Graphics gr)
        {
            GraphicsPath path = new GraphicsPath();
            PointF[] DrawData = new PointF[Data.Length];
            for (int i = 0; i < Data.Length; i++)
            {
                DrawData[i].X = Middle.X + Data[i].X * _XStep;
                DrawData[i].Y = Middle.Y - Data[i].Y * _YStep;
            }
            path.AddLines(DrawData);
            gr.DrawPath(new Pen(Color.Red), path);
        }
        /// <summary>
        /// Koordinatenanzeige an der Maus zeichnen
        /// </summary>
        /// <param name="mg">Grafikkomponente zum zeichnen</param>
        private void _DrawMouseCoordinates(Graphics mg)
        {
            float _X = (_MousePos.X - Middle.X) / _XStep;
            float _Y = (Middle.Y - _MousePos.Y) / _YStep;
            mg.DrawString("[" + _X.ToString("F02") + " " + _XUnit + " ; " + _Y.ToString("F02") + " " + _YUnit + "]", new Font("Arial", 8), new SolidBrush(Color.Blue), _MousePos.X - 40, _MousePos.Y + 20); 
        }
        /// <summary>
        /// die Maus wird bewegt
        /// </summary>
        /// <param name="sender">Sender</param>
        /// <param name="e">Ereignisargumente</param>
        private void GraphicForm_MouseMove(object sender, MouseEventArgs e)
        {
            _MouseInWindow = true;
            //Position der Maus bestimmen
            _MousePos = e.Location;
            Invalidate(Bounds);
            //mauszeiger ändern, wenn er über den Cursor bewegt wird
            if (e.X >= (_CursorPos.X - 10) && e.X <= (_CursorPos.X + 10) && _XCursor)
            {
                Cursor = Cursors.SizeWE;
                _MouseOverCursor = true;
            }
            else
            {
                Cursor = Cursors.Default;
                _MouseOverCursor = false;
            }

            if (_MoveCursor)
            {
                _CursorPos.X = (e.X - Middle.X) / _XStep;
                Invalidate();
            }
        }
        /// <summary>
        /// Die Maus verlässt das Fenster
        /// </summary>
        /// <param name="sender">Sender</param>
        /// <param name="e">Ereignisargumente</param>
        private void GraphicForm_MouseLeave(object sender, EventArgs e)
        {
            _MouseInWindow = false;
            Invalidate();
        }
        /// <summary>
        /// Zeichnet den Messcursor
        /// </summary>
        private void _DrawMeasurementCursor(Graphics gm)
        {
            gm.DrawLine(new Pen(Color.Green), _CursorPos.X, Buttom.Y, _CursorPos.X, Top.Y);
        }
        /// <summary>
        /// Maustaste wurde gedrückt
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void GraphicForm_MouseDown(object sender, MouseEventArgs e)
        {
            if (_MouseOverCursor && e.Button == MouseButtons.Left)
            {
                _MoveCursor = true;
            }
        }
        /// <summary>
        /// Maustaste wird losgelassen
        /// </summary>
        /// <param name="sender">Sender</param>
        /// <param name="e">Ereignisargumente</param>
        private void GraphicForm_MouseUp(object sender, MouseEventArgs e)
        {
            _MoveCursor = false;
        }
        #endregion
    }
}
